import path from 'node:path';
import { describe, expect, layer } from '@effect/vitest';
import { Effect } from 'effect';
import { FileSystem } from '@effect/platform';
import { cli, TestLive } from 'test/__utils__';
import { makeTestToolkits } from 'test/__utils__/models/toolkits';
import { NodeProcess } from 'src/services/node-process';
import { TRIGGER_TYPES_GMAIL } from 'test/__mocks__/trigger-types-gmail';
import { TestLiveInput } from 'test/__utils__/services/test-layer';
import { assertPythonIsValid } from 'test/__utils__/python-compiler';
import { TOOLS_TYPES_GMAIL } from 'test/__mocks__/tools-types-gmail';

describe('CLI: composio py generate', () => {
  const appClientData = {
    toolkits: makeTestToolkits([
      {
        name: 'Gmail',
        slug: 'gmail',
      },
      {
        name: 'Slack',
        slug: 'slack',
      },
    ]),
    tools: [...TOOLS_TYPES_GMAIL.slice(0, 3)],
    triggerTypesAsEnums: [...TRIGGER_TYPES_GMAIL.slice(0, 3).map(triggerType => triggerType.slug)],
    triggerTypes: [...TRIGGER_TYPES_GMAIL.slice(0, 3)],
  } satisfies TestLiveInput['toolkitsData'];

  layer(
    TestLive({
      fixture: 'python-project-with-composio-core',
      toolkitsData: appClientData,
    })
  )(it => {
    describe('[Given] valid fetched app data', () => {
      it.scoped.skip(
        '[Given] no arguments [Then] it generates type stubs relative the `composio` module if detectable by uv',
        () =>
          Effect.gen(function* () {
            const process = yield* NodeProcess;
            const cwd = process.cwd;
            const fs = yield* FileSystem.FileSystem;

            const args = ['py', 'generate'];
            yield* cli(args);

            const outputDir = path.join(
              cwd,
              '.venv',
              'lib',
              'python3.12',
              'site-packages',
              'composio'
            );

            const gmailSourceCode = yield* fs.readFileString(path.join(outputDir, 'gmail.py'));
            const slackSourceCode = yield* fs.readFileString(path.join(outputDir, 'slack.py'));

            expect(gmailSourceCode).toMatchInlineSnapshot(`
              "# Auto-generated by Composio CLI via \`composio py generate\`.
              # Do not modify manually.

              class GMAIL:
                  """Map of Composio's GMAIL toolkit."""

                  slug: str = "gmail"

                  class tools:
                      CREATE_EMAIL_DRAFT = "GMAIL_CREATE_EMAIL_DRAFT"
                      DELETE_MESSAGE = "GMAIL_DELETE_MESSAGE"
                      FETCH_EMAILS = "GMAIL_FETCH_EMAILS"

                  class triggers:
                      NEW_GMAIL_MESSAGE = "GMAIL_NEW_GMAIL_MESSAGE"
              "
            `);
            expect(slackSourceCode).toMatchInlineSnapshot(`
              "# Auto-generated by Composio CLI via \`composio py generate\`.
              # Do not modify manually.

              class SLACK:
                  """Map of Composio's SLACK toolkit."""

                  slug: str = "slack"

                  class tools:
                      pass

                  class triggers:
                      pass
              "
            `);

            assertPythonIsValid({ files: { 'gmail.py': gmailSourceCode } });
            assertPythonIsValid({ files: { 'slack.py': slackSourceCode } });
            assertPythonIsValid({
              files: {
                'gmail.py': gmailSourceCode,
                'slack.py': slackSourceCode,
              },
            });
          })
      );

      it.scoped(
        '[Given] --output-dir [Then] it generates type stubs relative to the given output directory',
        Effect.fn(function* () {
          const process = yield* NodeProcess;
          const cwd = process.cwd;
          const fs = yield* FileSystem.FileSystem;

          const outputDir = path.join(cwd, '.generated', 'composio-py');

          const args = ['py', 'generate', '--output-dir', outputDir];
          yield* cli(args);

          const gmailSourceCode = yield* fs.readFileString(path.join(outputDir, 'gmail.py'));
          const slackSourceCode = yield* fs.readFileString(path.join(outputDir, 'slack.py'));

          expect(gmailSourceCode).toMatchInlineSnapshot(`
            "# Auto-generated by Composio CLI. Do not modify manually..

            class GMAIL:
                """Map of Composio's GMAIL toolkit."""

                slug: str = "gmail"

                class tools:
                    pass

                class triggers:
                    NEW_GMAIL_MESSAGE = {
                      "config": {
                        "properties": {
                          "interval": {
                            "default": 1,
                            "description": "Periodic Interval to Check for Updates & Send a Trigger in Minutes",
                            "title": "Interval",
                            "type": "number"
                          },
                          "labelIds": {
                            "default": "INBOX",
                            "description": "Filter messages by a single label ID. Labels identify the status or category of messages. Supported labels include 'INBOX', 'SPAM', 'TRASH', 'UNREAD', 'STARRED', 'IMPORTANT', 'CATEGORY_PERSONAL', 'CATEGORY_SOCIAL', 'CATEGORY_PROMOTIONS', 'CATEGORY_UPDATES', and 'CATEGORY_FORUMS'. For complex label filtering, use the 'query' parameter instead.",
                            "examples": ["INBOX", "UNREAD", "STARRED"],
                            "title": "Labelids",
                            "type": "string"
                          },
                          "query": {
                            "default": "",
                            "description": "Advanced Gmail search using the same syntax as Gmail's search box. Use 'AND' for messages that match all conditions, 'OR' for any condition. Search by sender (from:email@domain.com), labels (label:inbox), status (is:unread), attachments (has:attachment), dates (after:2023/1/1), and more. If specified, this takes precedence over labelIds.",
                            "examples": ["label:inbox OR label:sent", "from:example@gmail.com is:unread", "has:attachment after:2023/1/1", "is:important is:unread"],
                            "title": "Query",
                            "type": "string"
                          },
                          "userId": {
                            "default": "me",
                            "description": "The user's email address or 'me' for the authenticated user.",
                            "examples": ["me"],
                            "title": "Userid",
                            "type": "string"
                          }
                        },
                        "title": "NewMessageConfig",
                        "type": "object"
                      },
                      "description": "Triggers when a new message is received in Gmail.",
                      "instructions": "\\n    **Instructions for Setting Up the Trigger:**\\n\\n    - Ensure that the Gmail API is enabled for your Google account.\\n    - Provide the user ID (usually 'me' for the authenticated user).\\n    - Optionally, provide label IDs to filter messages.\\n    ",
                      "name": "New Gmail Message Received Trigger",
                      "payload": {
                        "properties": {
                          "attachment_list": {
                            "anyOf": [{
                                "items": {},
                                "type": "array"
                              }, {
                                "type": "null"
                              }],
                            "default": None,
                            "description": "The list of attachments in the message",
                            "title": "Attachment List"
                          },
                          "message_id": {
                            "anyOf": [{
                                "type": "string"
                              }, {
                                "type": "null"
                              }],
                            "default": None,
                            "description": "The message ID of the message",
                            "title": "Message ID"
                          },
                          "message_text": {
                            "anyOf": [{
                                "type": "string"
                              }, {
                                "type": "null"
                              }],
                            "default": None,
                            "description": "The text of the message",
                            "title": "Message Text"
                          },
                          "message_timestamp": {
                            "anyOf": [{
                                "type": "string"
                              }, {
                                "type": "null"
                              }],
                            "default": None,
                            "description": "The timestamp of the message",
                            "title": "Message Timestamp"
                          },
                          "payload": {
                            "anyOf": [{
                                "type": "object"
                              }, {
                                "type": "null"
                              }],
                            "default": None,
                            "description": "The payload of the message",
                            "title": "Payload"
                          },
                          "sender": {
                            "anyOf": [{
                                "type": "string"
                              }, {
                                "type": "null"
                              }],
                            "default": None,
                            "description": "The sender of the message",
                            "title": "Sender"
                          },
                          "subject": {
                            "anyOf": [{
                                "type": "string"
                              }, {
                                "type": "null"
                              }],
                            "default": None,
                            "description": "The subject of the message",
                            "title": "Subject"
                          },
                          "thread_id": {
                            "anyOf": [{
                                "type": "string"
                              }, {
                                "type": "null"
                              }],
                            "default": None,
                            "description": "The thread ID of the message",
                            "title": "Thread ID"
                          },
                          "to": {
                            "anyOf": [{
                                "type": "string"
                              }, {
                                "type": "null"
                              }],
                            "default": None,
                            "description": "The recipient of the message",
                            "title": "To"
                          }
                        },
                        "title": "NewMessagePayload",
                        "type": "object"
                      },
                      "slug": "GMAIL_NEW_GMAIL_MESSAGE",
                      "type": "poll"
                    }
            "
          `);
          expect(slackSourceCode).toMatchInlineSnapshot(`
              "# Auto-generated by Composio CLI. Do not modify manually..

              class SLACK:
                  """Map of Composio's SLACK toolkit."""

                  slug: str = "slack"

                  class tools:
                      pass

                  class triggers:
                      pass
              "
            `);

          assertPythonIsValid({ files: { 'gmail.py': gmailSourceCode } });
          assertPythonIsValid({ files: { 'slack.py': slackSourceCode } });
          assertPythonIsValid({
            files: {
              'gmail.py': gmailSourceCode,
              'slack.py': slackSourceCode,
            },
          });
        })
      );
    });
  });
});
